    /**
     * Lin-Validator v2
     * 作者：7七月
     * 微信公众号：林间有风
     */

    const validator = require('validator')
    const {
        ParameterException
    } = require('./http-exception')
    const {
        get,
        last,
        set,
        cloneDeep
    } = require("lodash")
    const {
        findMembers
    } = require('./util')


    class LinValidator {
        constructor() {
            this.data = {}
            this.parsed = {}
        }


        _assembleAllParams(ctx) {
            return {
                body: ctx.request.body,
                query: ctx.request.query,
                path: ctx.params,
                header: ctx.request.header
            }
        }

        get(path, parsed = true) {
            if (parsed) {
                const value = get(this.parsed, path, null)
                if (value == null) {
                    const keys = path.split('.')
                    const key = last(keys)
                    return get(this.parsed.default, key)
                }
                return value
            } else {
                return get(this.data, path)
            }
        }

        _findMembersFilter(key) {
            if (/validate([A-Z])\w+/g.test(key)) {
                return true
            }
            if (this[key] instanceof Array) {
                this[key].forEach(value => {
                    const isRuleType = value instanceof Rule
                    if (!isRuleType) {
                        throw new Error('验证数组必须全部为Rule类型')
                    }
                })
                return true
            }
            return false
        }

        async validate(ctx, alias = {}) {
            this.alias = alias
            let params = this._assembleAllParams(ctx)
            this.data = cloneDeep(params)
            this.parsed = cloneDeep(params)

            const memberKeys = findMembers(this, {
                filter: this._findMembersFilter.bind(this)
            })

            const errorMsgs = []
            // const map = new Map(memberKeys)
            for (let key of memberKeys) {
                const result = await this._check(key, alias)
                if (!result.success) {
                    errorMsgs.push(result.msg)
                }
            }
            if (errorMsgs.length != 0) {
                throw new ParameterException(errorMsgs)
            }
            ctx.v = this
            return this
        }

        async _check(key, alias = {}) {
            const isCustomFunc = typeof (this[key]) == 'function' ? true : false
            let result;
            if (isCustomFunc) {
                try {
                    await this[key](this.data)
                    result = new RuleResult(true)
                } catch (error) {
                    result = new RuleResult(false, error.msg || error.message || '参数错误')
                }
                // 函数验证
            } else {
                // 属性验证, 数组，内有一组Rule
                const rules = this[key]
                const ruleField = new RuleField(rules)
                // 别名替换
                key = alias[key] ? alias[key] : key
                const param = this._findParam(key)

                result = ruleField.validate(param.value)

                if (result.pass) {
                    // 如果参数路径不存在，往往是因为用户传了空值，而又设置了默认值
                    if (param.path.length == 0) {
                        set(this.parsed, ['default', key], result.legalValue)
                    } else {
                        set(this.parsed, param.path, result.legalValue)
                    }
                }
            }
            if (!result.pass) {
                const msg = `${isCustomFunc ? '' : key}${result.msg}`
                return {
                    msg: msg,
                    success: false
                }
            }
            return {
                msg: 'ok',
                success: true
            }
        }

        _findParam(key) {
            let value
            value = get(this.data, ['query', key])
            if (value) {
                return {
                    value,
                    path: ['query', key]
                }
            }
            value = get(this.data, ['body', key])
            if (value) {
                return {
                    value,
                    path: ['body', key]
                }
            }
            value = get(this.data, ['path', key])
            if (value) {
                return {
                    value,
                    path: ['path', key]
                }
            }
            value = get(this.data, ['header', key])
            if (value) {
                return {
                    value,
                    path: ['header', key]
                }
            }
            return {
                value: null,
                path: []
            }
        }
    }

    class RuleResult {
        constructor(pass, msg = '') {
            Object.assign(this, {
                pass,
                msg
            })
        }
    }

    class RuleFieldResult extends RuleResult {
        constructor(pass, msg = '', legalValue = null) {
            super(pass, msg)
            this.legalValue = legalValue
        }
    }

    class Rule {
        constructor(name, msg, ...params) {
            Object.assign(this, {
                name,
                msg,
                params
            })
        }

        validate(field) {
            if (this.name == 'isOptional')
                return new RuleResult(true)
            if (!validator[this.name](field + '', ...this.params)) {
                return new RuleResult(false, this.msg || this.message || '参数错误')
            }
            return new RuleResult(true, '')
        }
    }

    class RuleField {
        constructor(rules) {
            this.rules = rules
        }

        validate(field) {
            if (field == null) {
                // 如果字段为空
                const allowEmpty = this._allowEmpty()
                const defaultValue = this._hasDefault()
                if (allowEmpty) {
                    return new RuleFieldResult(true, '', defaultValue)
                } else {
                    return new RuleFieldResult(false, '字段是必填参数')
                }
            }

            const filedResult = new RuleFieldResult(false)
            for (let rule of this.rules) {
                let result = rule.validate(field)
                if (!result.pass) {
                    filedResult.msg = result.msg
                    filedResult.legalValue = null
                    // 一旦一条校验规则不通过，则立即终止这个字段的验证
                    return filedResult
                }
            }
            return new RuleFieldResult(true, '', this._convert(field))
        }

        _convert(value) {
            for (let rule of this.rules) {
                if (rule.name == 'isInt') {
                    return parseInt(value)
                }
                if (rule.name == 'isFloat') {
                    return parseFloat(value)
                }
                if (rule.name == 'isBoolean') {
                    return value ? true : false
                }
            }
            return value
        }

        _allowEmpty() {
            for (let rule of this.rules) {
                if (rule.name == 'isOptional') {
                    return true
                }
            }
            return false
        }

        _hasDefault() {
            for (let rule of this.rules) {
                const defaultValue = rule.params[0]
                if (rule.name == 'isOptional') {
                    return defaultValue
                }
            }
        }
    }



    module.exports = {
        Rule,
        LinValidator
    }
